﻿<# 
.DESCRIPTION 
    - Run this script to deploy/retract the farm level solution and activate/deactivate features

.GOAL
 - Build the most generic farm level soution deployment script
 
.ASSUMPTION
 - Batch files, log files, and WSPs exists in same directory during deployment process
 
.MAJOR FEATURES IMPLEMENTED
 - Parameters passed in from the batch file
 - Loading SharePoint PowerShell Plugin - Check if its already been loaded
 - Logging of Powershell script execution transcript, New file gets created for each run
 - Solution Deploy or Retract in single click
 - Waiting for  timer job execution while deploying and retracting solution
 - Nullable $siteFeatureNames and $webFeatureNames parameters to support deployment only option, no activation of features
 - Support Multiple Site and Web Level Features - Site feature and web features are passed in as array paramters
 - Support Activation of features at Multiple Webs - Activate/Deactivate Web Level features on all webs in site collection
 - Check for webapplication level resources to deploy or retract solutions at the web application level or all web application level
 - Prompt the script execution details with different color codes - green for major steps, yellow for detailed info, and red for errors

#>

param (
 [string]$solutionName = "$(Read-Host 'Enter the Solution WSP Name. [e.g. Sample.wsp]')",
 [string]$webAppUrl = "$(Read-Host 'Enter the Web Application URL. [e.g. http://sp2010vm]')",
 [string]$siteUrl = "$(Read-Host 'Enter the Site Collection URL. [e.g. http://sp2010vm/sites/site]')",
 [string]$webUrl = "$(Read-Host 'Enter the web URL. [e.g. http://sp2010vm/sites/site/subsite]')",
 [string[]]$siteFeatureNames = $null, #"$(Read-Host 'Enter the Site Level Features. [e.g. @('Feature1', 'Feature2')]')",
 [string[]]$webFeatureNames = $null, #"$(Read-Host 'Enter the Web Level Features. [e.g. @('Feature1', 'Feature2')]')",
 [string]$logFileName = "$(Read-Host 'Enter the Log File Name. [e.g. Sample.log]')",
 [string]$action = "$(Read-Host 'Enter [SD] to deploy solutions or [SR] to retract the solution')",
 [string]$directoryPath = $null,
 [string]$compatibilityLevel = "15"
)

# Defination of main function
function main() {
 # find the current directory
 if($directoryPath -eq $null) {
    $currentDirectory = Get-Location
 }
 else {
  $currentDirectory = $directoryPath
 }
   
 # delete existing logfile
 $logFilePath = "$currentDirectory\$logFileName"
 if (Test-Path $logFilePath)
 {
  Remove-Item $logFilePath
 }
 
 # create new log file and start logging
 Start-Transcript $logFilePath

 # check to ensure Microsoft.SharePoint.PowerShell is loaded
 $snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
 if ($snapin -eq $null)
 {
  Write-Host "Loading SharePoint Powershell Snapin"
  Add-PSSnapin "Microsoft.SharePoint.Powershell"
 }
 
 # deploy the solution
 if ($action -eq "SD")
 {
  Write-Host "Step 1 - Add Solution Package: " $solutionName -foregroundcolor Green
  AddSolution
  
  Write-Host "Step 2 - Deploy Solution Package: " $solutionName -foregroundcolor Green
  InstallSolution
  
  Write-Host "Step 3 - Wait until Solution Deployment Timer Job completion" -foregroundcolor Green
  Wait4TimerJob

  Write-Host "Step 4 - Activate Site Collection Level Features" -foregroundcolor Green
  ActivateSiteFeatures
  
  Write-Host "Step 5 - Activate Web Level Features" -foregroundcolor Green
  ActivateWebFeatures2
 } 
 
 # retract the solution
 if ($action -eq "SR")
 {
  Write-Host "Step 1 - Deactivate Web Level Features" -foregroundcolor Green
  DeactivateWebFeatures2
  
  Write-Host "Step 2 - Deactivate Site Collection Level Features" -foregroundcolor Green
  DeactivateSiteFeatures
  
  Write-Host "Step 3 - Uninstall Solution Package: " $solutionName -foregroundcolor Green
  UnInstallSolution
  
  Write-Host "Step 4 - Timer Job to Retract the Solution Package" -foregroundcolor Green
  Wait4TimerJob
  
  Write-Host "Step 5 - Remove Solution Package: " $solutionName -foregroundcolor Green
  RemoveSolution
 }
 
 # stop the logging
 Stop-Transcript
}

# Add the solution package
# Adds a SharePoint solution package to the farm Solution gallery, It doesn't deploy the uploaded solution yet.
function AddSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 if ($solution -eq $null)
 {
  Write-Host "Adding solution package" -foregroundcolor Yellow
  $solutionPath = "$currentDirectory\$solutionName"
  Add-SPSolution -LiteralPath $solutionPath -Confirm:$false
 }
}

# Deploy the solution package 
# Deploys an installed SharePoint solution on all the WFE servers in the farm.
# Deploying solution in the farm installs the feature automatically. It installs all the features
# on each server at the farm, web, site collection, and site level but It doesn't activate them.
# Since it provisions the file on each WFE, it is a timer job.
function InstallSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 $solutionId = $solution.Id
 if ($solution -ne $null)
 {
  $solutionDeployed = Get-SPSolution -Identity $solutionId | where-object {$_.Deployed -eq "False"}
  if ($solutionDeployed -eq $null)
  {
   if ( $solution.ContainsWebApplicationResource )
   {
    Write-Host "Deploying solution package to web application: " $webAppUrl -foregroundcolor Yellow
    Install-SPSolution -Identity $solutionName -WebApplication $webAppUrl -GACDeployment -Confirm:$false -FullTrustBinDeployment -CompatibilityLevel $compatibilityLevel
   }
   else
   {
    Write-Host "Deploying solution package to all web applications" -foregroundcolor Yellow
    Install-SPSolution -Identity $solutionName -GACDeployment -Confirm:$false -CompatibilityLevel $compatibilityLevel
   }
  }
 }
}

# Activate the Site level features
function ActivateSiteFeatures()
{ 
 if ($siteFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl  
  foreach($siteFeatureName in $siteFeatureNames)
  {
   Write-Host "Trying to Activate Site Collection Level Feature: " $siteFeatureName -foregroundcolor Yellow
   $siteFeature = Get-SPFeature -site $spSite.url | where-object {$_.displayname -eq $siteFeatureName}
   if ($siteFeature -eq $null)
   {
    Write-Host "Activating Site Level Features at " $spSite.url -foregroundcolor Yellow
    Enable-SPFeature –identity $siteFeatureName -URL $spSite.url -Confirm:$false
   }
  }  
  $spSite.Dispose()
 }
}

  

# Activate the Web level features
function ActivateWebFeatures()
{
 if ($webFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl
 
  #Cycle through all webs in the collection and activate all the features
  foreach($spWeb in $spSite.AllWebs)
  { 
   foreach($webFeatureName in $webFeatureNames)
   {  
    Write-Host "Trying to Activate Web Level Features: " $webFeatureName -foregroundcolor Yellow
    $webFeature = Get-SPFeature -web $spWeb.url | where-object {$_.displayname -eq $webFeatureName} 
    if ($webFeature -eq $null)
    {
     Write-Host "Activating " $webFeatureName " at " $spWeb.url -foregroundcolor Yellow
     Enable-SPFeature –identity $webFeatureName -URL $spWeb.url -Confirm:$false
    }
   }
  }
  
  $spWeb.Dispose()
  $spSite.Dispose()
 }
}

function ActivateWebFeatures2()
{
 if ($webFeatureNames -ne $null)
 {
  $spWeb = Get-SPWeb $webUrl
 	foreach($webFeatureName in $webFeatureNames)
   {
    Write-Host "Trying to Activate Web Level Features: " $webFeatureName -foregroundcolor Yellow
    $webFeature = Get-SPFeature -web $spWeb.url | where-object {$_.displayname -eq $webFeatureName} 
    if ($webFeature -eq $null)
    {
     Write-Host "Activating " $webFeatureName " at " $spWeb.url -foregroundcolor Yellow
     Enable-SPFeature –identity $webFeatureName -URL $spWeb.url -Confirm:$false
    }
   }
   $spWeb.Dispose()
  }
  
  

}

# Deactivate the Web level features
function DeactivateWebFeatures()
{
 if ($webFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl
 
  #Cycle through all webs in the collection and deactivate all the features
  foreach($spWeb in $spSite.AllWebs)
  { 
   foreach($webFeatureName in $webFeatureNames)
   {  
    Write-Host "Trying to Deactivate Web Level Features: " $webFeatureName -foregroundcolor Yellow
    $webFeature = Get-SPFeature -web $spWeb.url | where-object {$_.displayname -eq $webFeatureName} 
    if ($webFeature -ne $null)
    {
     Write-Host "Deactivating " $webFeatureName " at " $spWeb.url -foregroundcolor Yellow
     Disable-SPFeature –identity $webFeatureName -URL $spWeb.url -Confirm:$false
    }
   }
  }
  
  $spWeb.Dispose()
  $spSite.Dispose()
 }
}

function DeactivateWebFeatures2()
{
 if ($webFeatureNames -ne $null)
 {
  $spWeb = Get-SPWeb $webUrl
 
  
   foreach($webFeatureName in $webFeatureNames)
   {  
    Write-Host "Trying to Deactivate Web Level Features: " $webFeatureName -foregroundcolor Yellow
    $webFeature = Get-SPFeature -web $spWeb.url | where-object {$_.displayname -eq $webFeatureName} 
    if ($webFeature -ne $null)
    {
     Write-Host "Deactivating " $webFeatureName " at " $spWeb.url -foregroundcolor Yellow
     Disable-SPFeature –identity $webFeatureName -URL $spWeb.url -Confirm:$false
    }
   }
 
  
  $spWeb.Dispose()
 
 }
}

# Deactivate the Site level features
function DeactivateSiteFeatures()
{
 if ($siteFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl
  foreach($siteFeatureName in $siteFeatureNames)
  {
   Write-Host "Trying to Deactivate Site Collection Level Feature: " $siteFeatureName -foregroundcolor Yellow
   $siteFeature = Get-SPFeature -site $spSite.url | where-object {$_.displayname -eq $siteFeatureName}
   if ($siteFeature -ne $null)
   {
    Write-Host "Deactivating Site Level Features at " $spSite.url -foregroundcolor Yellow
    Disable-SPFeature –identity $siteFeatureName -URL $spSite.url -Confirm:$false
   }  
  } 
  $spSite.Dispose()
 }
}

# Retract the solution package
# Retracts a deployed SharePoint solution from the farm entirely for all web application or given web application.
# This step removes files from all the front-end Web server.
# Please note that retracting solution in the farm uninstalls the feature automatically, if it hasn't uninstalled using UnInstall-SPFeature.
# Since it removes the file on each WFE, it is a timer job.
function UnInstallSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 $solutionId = $solution.Id
 if ($solution -ne $null)
 {
  $solutionDeployed = Get-SPSolution -Identity $solutionId | where-object {$_.Deployed -eq "True"}
  if ($solutionDeployed -ne $null)   
  { 
   if ( $solution.ContainsWebApplicationResource )
   {
    Write-Host "Retracting solution package from web application: " $webAppUrl -foregroundcolor Yellow
    UnInstall-SPSolution -Identity $solutionName -WebApplication $webAppUrl -Confirm:$false
   }
   else
   {
    Write-Host "Retracting solution package from all web applications" -foregroundcolor Yellow
    UnInstall-SPSolution -Identity $solutionName -Confirm:$false
   }
  }
 }
}

# Remove the solution package
# Deletes a SharePoint solution from a farm solution gallery
function RemoveSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 if ($solution -ne $null)
 {
  Write-Host "Deleting the solution package" -foregroundcolor Yellow
  Remove-SPSolution $solutionName -Confirm:$false
 }
}

# Wait for timer job during deploy and retract
function Wait4TimerJob()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 if ($solution -ne $null)
 {
  $counter = 1  
  $maximum = 50  
  $sleeptime = 2 
  
  Write-Host "Waiting to finish solution timer job"
  while( ($solution.JobExists -eq $true ) -and ( $counter -lt $maximum ) )
  {  
   Write-Host "Please wait..."
   sleep $sleeptime 
   $counter++  
  }
  
  Write-Host "Finished the solution timer job"   
 }
}

#Run the Main Function
main

